// Effective C++ 条款25：考虑写出一个不抛异常的swap函数

// 接下来让我为你讲解Effective C++中的条款25，这是关于swap函数的重要设计原则。
// 核心原则
// 为你的类型提供一个高效的swap函数，并确保它不抛出异常。
// 为什么swap函数很重要
// swap函数在C++中有多种重要用途：
// 1.实现强异常安全性保证：许多类使用"copy-and-swap"技术来提供异常安全的赋值操作
// 2.STL算法的基础操作：许多算法依赖swap来重新排列元素
// 3.资源管理的关键工具：用于实现移动语义和资源转移

// 标准swap的局限性
// 标准库提供了一个通用的swap实现：
namespace std
{
    template <typename T>
    void swap(T &a, T &b)
    {
        T temp(a);
        a = b;
        b = temp;
    }
}

// 这个实现有三个问题：
// 1.它进行了三次复制操作，对于某些类型可能效率很低
// 2.它要求类型可复制
// 3.对于"pimpl idiom"（指针实现技术）类，这种交换方式效率低下

// 为自定义类型优化swap

// 假设widget包含大量数据：
class Widget
{
private:
    std::vector<double> largeData; // 可能包含数百万个元素
    std::string name;
    // 更多成员...
};
// 使用标准swap交换两个Widget对象会：
// 复制整个largeData向量三次
// 复制name字符串三次
// 复制所有其他成员三次

// 1. 对于使用pimpl技术的类
class Widget
{
public:
    // 构造函数、析构函数等...

    void swap(Widget &other) noexcept
    {
        std::swap(pImpl, other.pImpl); // 只交换指针，非常高效
    }

private:
    struct Impl; // 前向声明
    Impl *pImpl; // 指向实现的指针
};

// 非成员函数版本
void swap(Widget &a, Widget &b) noexcept
{
    a.swap(b); // 调用成员函数版本
}

// 2. 正确的命名空间设计
class Widget
{
    ...
}; // 同上

namespace WidgetStuff
{
    // 在自己的命名空间中声明Widget
    class Widget
    {
        ...
    };

    // 在同一命名空间中提供非成员swap
    void swap(Widget &a, Widget &b) noexcept
    {
        a.swap(b);
    }
}
// 命名空间设计在swap中的重要性
// 命名空间与ADL（参数依赖查找）
// 首先，我们需要理解C++中的一个重要机制：参数依赖查找（Argument-Dependent Lookup，简称ADL）。
// ADL是C++中的一个特性，它允许编译器在函数参数的命名空间中查找函数名。这对于swap函数特别重要。
// ADL的工作原理
// 当你调用一个不带命名空间限定符的函数时，C++会：

// 在当前作用域中查找该函数
// 在函数参数类型所在的命名空间中查找该函数
// 为什么命名空间设计对swap很重要
// 在swap的情况下，正确的命名空间设计能确保：
// 1.用户可以简单地调用swap(a, b)而不是std::swap(a, b)
// 2.编译器会自动选择最优化的swap实现
// 3.你的自定义swap会被优先使用，而不是标准库的通用实现

// 具体例子说明
// 假设我们有一个自定义类型Widget，放在命名空间WidgetLib中：
namespace WidgetLib
{
    class Widget
    {
    public:
        void swap(Widget &other) noexcept
        {
            // 高效的swap实现
        }

        // 其他成员...
    private:
        struct Impl;
        Impl *pImpl;
    };

    // 关键点：在同一命名空间中定义非成员swap
    void swap(Widget &a, Widget &b) noexcept
    {
        a.swap(b);
    }
    WidgetLib::Widget a, b;

}

// 正确使用方式
// 现在，当用户这样调用swap时：
// 推荐的调用方式
using std::swap; // 使标准swap可见
swap(a, b);      // 不带命名空间限定符

// 这里发生了什么：
// using std::swap使标准库的swap在当前作用域可见
// 调用swap(a, b)时，编译器通过ADL在WidgetLib命名空间中查找swap
// 编译器找到WidgetLib::swap并使用它，而不是std::swap
// 结果是调用了我们高效的自定义swap实现

// 错误的命名空间设计
// 如果我们没有在WidgetLib命名空间中提供非成员swap：

// 当用户调用swap(a, b) 时：

// ADL会在WidgetLib命名空间中查找swap，但找不到
// 全局命名空间的swap可能会被找到，但这取决于具体的编译器和上下文
// 在某些情况下，可能会回退到使用std::swap的通用实现

// 更糟糕的情况：命名空间污染
// 如果我们尝试特化std::swap：
namespace std
{
    template <>
    void swap<WidgetLib::Widget>(WidgetLib::Widget &a, WidgetLib::Widget &b) noexcept
    {
        a.swap(b);
    }
}
// 这会导致：
// 向std命名空间添加代码，这被认为是不好的实践
// 可能违反某些C++ 标准的规定 在不同的编译器上可能有不同的行为

// 实际例子：命名空间的正确使用
// 让我们看一个完整的例子，展示正确的命名空间设计：
// MyLibrary.h
namespace MyLib
{
    class DataContainer
    {
    public:
        DataContainer();
        ~DataContainer();

        // 其他成员函数...

        void swap(DataContainer &other) noexcept;

    private:
        struct Impl;
        Impl *pImpl;
    };

    // 在同一命名空间中声明非成员swap
    void swap(DataContainer &a, DataContainer &b) noexcept;
}

// MyLibrary.cpp
#include "MyLibrary.h"

namespace MyLib
{
    // 实现细节...

    void DataContainer::swap(DataContainer &other) noexcept
    {
        using std::swap;
        swap(pImpl, other.pImpl);
    }

    // 在同一命名空间中定义非成员swap
    void swap(DataContainer &a, DataContainer &b) noexcept
    {
        a.swap(b);
    }
}

// 客户代码
void clientCode()
{
    MyLib::DataContainer a, b;

    // 正确的调用方式
    using std::swap; // 使标准swap可见
    swap(a, b);      // ADL会找到MyLib::swap
}